/*
 * ecm.h - libecm internal header
 * Clunix, cezanne@clunix.com, 2004.10
 *
 * $Id: ecm.h,v 1.1.1.1 2007/10/01 01:05:18 cezanne Exp $
 *
 * Copyright (c) 2004-2005 by Clunix Inc. All Rights Reserved.
 */

#ifndef _ECMLIB_P_H_
#define _ECMLIB_P_H_

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <time.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <assert.h>

#ifndef _WIN32

#include <sys/time.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#else

#include <process.h>

#endif

#define _ECM_INTERNAL

#include "./libbase.h"
#include "./ecm_types.h"
#include "./ecm_list.h"
#include "./ecm_errno.h"
#include "./print.h"
#include "./safestr.h"

#define ECM_CONFIG_FILENAME		"ECM.conf"

#ifndef ECM_DEBUG
#define ECM_RELEASE_STATIC	static
#else
#define ECM_RELEASE_STATIC
#endif

#define ECM_RETURN_IF(expr)	do { if (expr) return; } while (0)
#define ECM_RETURN_VAL_IF(expr, val)	do { if (expr) return (val); } while (0)
#define ECM_MAX(a, b)  (((a) > (b)) ? (a) : (b))

#define ECM_CLX_HOME_ENV	"CLX_HOME"
#define ECM_CLXDEV_HOME_ENV	"CLXDEV_HOME"
#define ECM_EBC_PORT_ENV	"EBC_PORT"

#ifdef _WIN32
#define ECM_INT64_FMT	"I64"
#define ECM_INT64_FMT_LEN	3
#else
#if defined(ECM_ARCH_X86) || defined(ECM_ARCH_PPC)
#define ECM_INT64_FMT	"ll"
#define ECM_INT64_FMT_LEN	2
#else
#ifdef ECM_ARCH_X86_64
#define ECM_INT64_FMT	"l"
#define ECM_INT64_FMT_LEN	1
#endif
#endif
#endif

#ifdef __cplusplus
extern "C" {
#endif

typedef void *ptr_t;

typedef int (*ecm_comparefunc_t)(const void *, const void *);
typedef int (*ecm_comparedatafunc_t)(const void *, const void *, void *);
typedef int (*ecm_dtornotify_t)(void *);
typedef ECMBOOL (*ecm_traversefunc_t)(void *, void *, void *);

/**********************************************************************
  SOCKET Related 
 **********************************************************************/

ECMBOOL ecm_inet_aton(const char *ipstr, struct in_addr *paddr);

#include "./hash_table.h"

/**********************************************************************
  TIME Related 
 **********************************************************************/

#ifndef _WIN32

#define	ecm_timeval	timeval

#else

struct ecm_timeval
{
	long tv_sec;
	long tv_usec;
};

#endif

#define ecm_timeval_gt(a, b)	((ECMBOOL)((a).tv_sec > (b).tv_sec ||	\
	((a).tv_sec == (b).tv_sec && (a).tv_usec > (b).tv_usec)))

struct ecm_timeval ecm_tv_to_timeval(time_t tv);
time_t ecm_timeval_to_tv(struct ecm_timeval tv);
void ecm_msec_to_tv(ecm_uint32 msec, struct ecm_timeval *ptv);
void ecm_gettimeofday(struct ecm_timeval *ptv);
void ecm_timeval_add(struct ecm_timeval *a, struct ecm_timeval *b);
void ecm_timeval_sub(struct ecm_timeval *a, struct ecm_timeval *b);

int ecm_license_check(void);  /* load license key and set limits */
int ecm_license_check_node(int node_num);
int ecm_license_check_expire(int cur_time_sec);
int ecm_license_get_level(void);
int ecm_license_get_info(char *key, char *info_ret);


/**********************************************************************
  MEMORY Related 
 **********************************************************************/

typedef void *ecm_tree_t;
typedef void *ecm_stack_t;

typedef enum
{
	ECM_IN_ORDER,
	ECM_PRE_ORDER,
	ECM_POST_ORDER,
	ECM_LEVEL_ORDER
} ecm_traversetype_t;

#ifdef ECM_MTRACE

typedef enum {
	ECM_MTRINFO_MEM = 0,
	ECM_MTRINFO_PTRS,
	ECM_MTRINFO_HASHTABLE,
	ECM_MTRINFO_SR,
	ECM_MTRINFO_MUTEX
} ecm_mtrinfo_type_t;

#define ECM_MTRACE_MAX_CALLERS	16

typedef struct {
	ecm_mtrinfo_type_t	type;
	ecm_cptr_t	ptr;
	ecm_size_t	size;
	ecm_ptr_t	callers[ECM_MTRACE_MAX_CALLERS];
} ecm_mtrinfo_t;

typedef struct {
	char		*meminfo;
	char		*callers[ECM_MTRACE_MAX_CALLERS];
} ecm_mtrace_t;

typedef struct {
	ecm_uint32	mem_used, mem_used_per_thread;
	ecm_uint32	ptrs_cnt, ptrs_cnt_per_thread;
	ecm_uint32	hash_cnt, hash_cnt_per_thread;
	ecm_uint32	mutex_cnt, mutex_cnt_per_thread;
} ecm_memused_t;

void ECM_MEM_TRACE(ECMBOOL alloc, ecm_mtrinfo_type_t type, ecm_cptr_t ptr, ecm_size_t size);
void ECM_MTRACE_BLOCK(void);
void ECM_MTRACE_UNBLOCK(void);
void ECM_MTRACE_LOCK(void);
void ECM_MTRACE_UNLOCK(void);

ecm_ptrs_t *ecm_ptrs_new_internal(void);
void ecm_ptrs_free_internal(ecm_ptrs_t *);
ecm_hashtable_t ecm_hashtable_create_internal(void);
void ecm_hashtable_finalize_internal(ecm_hashtable_t);
int ecm_asprintf_internal(char **pstr, const char *, ...);

void ecm_get_memused(ecm_memused_t *pmemused);

void ecm_mtrace(ECMBOOL self_trace_mode, ecm_uint32 level);
void ecm_muntrace(void);
void ecm_mtrace_clear(void);
ECMBOOL ecm_is_mtraced(void);

ecm_ptrs_t *ecm_get_mtrinfos(void);
void ecm_clear_mtrinfos(ecm_ptrs_t *);
ecm_ptrs_t *ecm_get_mtraces(void);
void ecm_clear_mtraces(ecm_ptrs_t *);

#else

#define ECM_MEM_TRACE(a,b,c,d)	do {} while (0)
#define ECM_MTRACE_BLOCK()	do {} while (0)
#define ECM_MTRACE_UNBLOCK()	do {} while (0)
#define ECM_MTRACE_LOCK()	do {} while (0)
#define ECM_MTRACE_UNLOCK()	do {} while (0)

#endif

ecm_tree_t ecm_tree_new(ecm_comparefunc_t key_compare_func);
ecm_tree_t ecm_tree_new_with_data(ecm_comparedatafunc_t key_compare_func,
				  void *key_compare_data);
ecm_tree_t ecm_tree_new_full(ecm_comparedatafunc_t key_compare_func,
			     void *key_compare_data,
			     ecm_dtornotify_t key_destroy_func,
			     ecm_dtornotify_t value_destroy_func);
void ecm_tree_destroy(ecm_tree_t ecm_tree);
void ecm_tree_insert(ecm_tree_t ecm_tree, void *key, void *value);
void ecm_tree_replace(ecm_tree_t ecm_tree, void *key, void *value);
void ecm_tree_remove(ecm_tree_t ecm_tree, const void *key);
void ecm_tree_steal(ecm_tree_t ecm_tree, const void *key);
void *ecm_tree_lookup(ecm_tree_t ecm_tree, const void *key);
ECMBOOL ecm_tree_lookup_extended(ecm_tree_t ecm_tree,
				 const void *lookup_key,
				 void **orig_key, void **value);
void ecm_tree_foreach(ecm_tree_t ecm_tree, 
		      ecm_traversefunc_t func, void *user_data);
void ecm_tree_traverse(ecm_tree_t ecm_tree,
		       ecm_traversefunc_t traverse_func,
		       ecm_traversetype_t traverse_type, void *user_data);
void *ecm_tree_search(ecm_tree_t ecm_tree, ecm_comparefunc_t search_func, 
		      const void *user_data);
int ecm_tree_height(ecm_tree_t ecm_tree);
int ecm_tree_nnodes(ecm_tree_t ecm_tree);

typedef ECMBOOL (*ecm_ptrs_walkfunc_t)(ecm_ptr_t ptr, ecm_ptr_t ctx);

ECMBOOL ecm_ptrs_increase(ecm_ptrs_t *ptrs, ecm_uint32 incr);
ecm_ptrs_t *ecm_ptrs_dup(ecm_ptrs_t *ptrs_src);
ecm_uint32 ecm_ptrs_add(ecm_ptrs_t *ptrs, ecm_ptr_t data);
ecm_uint32 ecm_ptrs_add_compact(ecm_ptrs_t *ptrs, ecm_ptr_t data);
ecm_uint32 ecm_ptrs_add_unique(ecm_ptrs_t *ptrs, ecm_ptr_t data);
void ecm_ptrs_insert(ecm_ptrs_t *ptrs, ecm_uint32 idx, ecm_ptr_t data);
ECMBOOL ecm_ptrs_walk(ecm_ptrs_t *ptrs, ecm_ptrs_walkfunc_t walkfunc, ecm_ptr_t ctx);
void ecm_ptrs_merge_unique(ecm_ptrs_t *ptrs, ecm_ptrs_t *ptrs_merged);
void ecm_ptrs_clear(ecm_ptrs_t *ptrs, ecm_size_t len);

ecm_stack_t ecm_stack_new(void);
void ecm_stack_free(ecm_stack_t stack);
void ecm_stack_push(ecm_stack_t stack, ecm_ptr_t data);
ecm_ptr_t ecm_stack_pop(ecm_stack_t stack);
ecm_ptr_t ecm_stack_top(ecm_stack_t stack);
ecm_ptr_t ecm_stack_peek(ecm_stack_t stack, ecm_uint32 level);

/**********************************************************************
  MISC Related 
 **********************************************************************/

extern ecm_uint32	ecm_n_envs;

char *ecm_ntoa(struct in_addr in);
ECMBOOL ecm_aton(const char *ipstr, struct in_addr *addr);

#ifndef HAVE_TIMERADD

#define timeradd(_a, _b, _res)                          \
  do {                                                  \
    (_res)->tv_usec = (_a)->tv_usec + (_b)->tv_usec;    \
    (_res)->tv_sec = (_a)->tv_sec + (_b)->tv_sec;       \
    if ((_res)->tv_usec >= 1000000)                     \
      {                                                 \
        (_res)->tv_usec -= 1000000;                     \
        (_res)->tv_sec++;                               \
      }                                                 \
  } while (0)

#define timersub(_a, _b, _res)                          \
  do {                                                  \
    (_res)->tv_usec = (_a)->tv_usec - (_b)->tv_usec;    \
    (_res)->tv_sec = (_a)->tv_sec - (_b)->tv_sec;       \
    if ((_res)->tv_usec < 0) {                          \
      (_res)->tv_usec += 1000000;                       \
      (_res)->tv_sec--;                                 \
    }                                                   \
  } while (0)

#define TIMEVAL_TO_TIMESPEC(tv, ts)                     \
  do {                                                  \
        (ts)->tv_sec = (tv)->tv_sec;                    \
        (ts)->tv_nsec = (tv)->tv_usec * 1000;           \
  } while (0)

#endif

#ifdef __cplusplus
}
#endif

#endif
